package org.crocodile.sbautologin;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import org.junit.Assert;
import org.junit.Test;

/**
 * Tests the HtmlForm class.
 * 
 * @author michael
 */
public class HtmlFormTest
{
    /**
     * The URL of the Starbucks login page.
     */
    private static final String url  = "https://securelogin.arubanetworks.com/";

    /**
     * The HTML of the login page.
     */
    private static final String html = "<html xmlns="http://www.w3.org/1999/xhtml"><head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
<title>Portal Login</title>
<style type="text/css">
form {padding: 0;margin: 10px;}
a:link, a:visited {color: #000;text-decoration: underline;}
#logo {display: none;}
#logins {padding: 0;margin-top: 155px;margin-left: 35px;}
#errorbox {font-weight: bold;color: #fff;border: 1px solid #fff;width: 190px;background-color: #c00;margin-bottom: 10px;padding: 2px;}
#instructions {position: absolute;left: 260px;top: 155px;color: #000;width: 350px;}
#instructionsbg {background-color: #ddd;-moz-opacity:0.5;    /* for mozilla */filter: alpha(style=0, opacity=50);/* for IE */opacity: 0.5;/* for safari */width: 352px;height: 302px;position: absolute;padding: 6px;}
#instructionstext {z-index: 100;color: #000;padding: 6px;position: absolute;border: 1px solid #fff;height: 300px;width: 350px;}
#registered {border: 1px solid #fff;width: 194px;color: #000;font-weight: bold;padding: 0;background-color: #ccc;margin-bottom: 15px;}
#passwordbox {margin-top: 5px;}
#guest {background-color: #ccc;border: 1px solid #fff;width: 194px;color: #000;font-weight: bold;padding: 0;}
.swap span, .swap u, .swap em {display: none;}
h1.swap, h2.swap, h3.swap, h4.swap, h5.swap {background: top left no-repeat;padding: 0;margin: 0;height: 25px;}
h1#registeredhdr { background-image: url(registereduser.gif); }
h1#guesthdr { background-image: url(guestuser.gif); }
#logins input {background-color: #eee;color: #000;}
#logins input.text {background-color: #eee;border: 1px solid #333;padding: 2px;width: 95%;}
#logins input.text:focus {padding: 2px;background-color: #fff;border: 1px solid #ee7d00;width: 95%;}
input.button {margin-top: 10px;border: 1px outset #eee;}
body {background: #000 url('/upload/custom/FRCCStudent-cp_prof/151-logo-only-no-background.gif') top left no-repeat;font-family: verdana, arial, helvetica, sans-serif;font-size: 11px;font-weight: normal;margin: 0;padding: 0;}
#user-agreement {font-family: verdana, arial, hevetica, sans-serif;font-size: 11px;font-weight: normal;padding:10px;margin:10px;width: 550px;border:1px dotted gray;background: gray;}
#user-agreement table, tr, td, input {font-family: verdana, arial, hevetica, sans-serif;font-size: 11px;font-weight: normal;}
#logins input {font-family: verdana, arial, hevetica, sans-serif;font-size: 11px;font-weight: normal;}
</style>
<script language="javascript" type="text/javascript">
function cp_ua() {
    this.div_ids = new Array('user-agreement', 'logins');
    this.cp_a_type = null;
    this.init = function(cp_a_type) {
        if(this.cp_a_type == null)
            this.cp_a_type = cp_a_type;}
    this.show_forms = function(isUa, isDisabled) {
        var __user_agreement = (!isUa) ? true : false;
        var __cmp_str = (__user_agreement) ? this.div_ids[0] : this.div_ids[1];
        var vm=null, vmid=null;
        for(var i=0; i<this.div_ids.length; i++) {
            var div_id = this.div_ids[i];
            var div_ref = document.getElementById(div_id);
            var disp_str = (div_id.match(__cmp_str)) ? 'block' : 'none';
            if(div_ref) {
                div_ref.style.display = disp_str;}}
        if(isUa && (vm = document.getElementsByName(this.cp_a_type)[0]) != null) {
            this.ed_fields(isDisabled);
            vm.focus();}}
    this.ed_fields = function(flag) {   /* Enable/Disable fields */
        var inputs = document.getElementsByTagName('input');
        for(var j=0; j<inputs.length; j++) {
            var vm = inputs[j];
            if(vm) {vm.disabled = flag;}}
        return; }
    this.only_aup = function(requested_url) {
        var xform = document.getElementById("only_aup_form");
        var url = document.getElementsByName("requested_url")[0];
        url.value = requested_url;
        xform.submit();
        return;}}
var CpUa = new cp_ua();
function showPolicy() {win = window.open("/upload/custom/FRCCStudent-cp_prof/acceptableusepolicy.html", "policy", "height=550,width=550,scrollbars=1");}
</script>
</head>
<body onload="document.getElementById('email').focus();" style="background-color: rgb(255, 255, 255);">
<div id="main">
    <div id="user-agreement" style="display: none">
        <table width="100%"><tbody>
          <tr><td>
            <h3>User Agreement Policy</h3>
          </td></tr>
          <tr><td>
          </td></tr>
          <tr><td align="center"><br>
        <input value="Accept" onclick="" type="button">
          </td></tr>
        </tbody></table>
    </div>
        <div id="logins">
                <div id="errorbox" style="display: none">
                 </div>
                <div id="registered" style="display: none">
                        <h1 id="registeredhdr" class="swap"><span>REGISTERED USER</span></h1>
                        <form action="login" id="regform" method="post" autocomplete="off" title="Form used by registered users to login">
                        <div id="usernamebox">
                        <label for="user" accesskey="u">USERNAME</label>
                        <input name="user" id="user" size="25" class="text" accesskey="u" type="text">
                        </div>
                        <div id="passwordbox">
                        <label for="password" accesskey="p">PASSWORD</label>
                        <input id="password" name="password" size="25" class="text" accesskey="p" type="password">
                        </div>
                        <div id="fqdnbox" style="display: none"></div>
                        <input name="cmd" value="authenticate" type="hidden">
                        <input name="Login" value="Log In" class="button" type="submit">
</form></div> <div style="display: none">
                        <form action="login" id="only_aup_form" method="post" title="">
                        <input name="accept_aup" value="accept_aup" type="hidden">
                        <input name="requested_url" value="" type="hidden">
                        </form>
                </div><div id="guest" style="">
                        <h1 id="guesthdr" class="swap"><span>GUEST USER</span></h1>
                        <form action="login" id="guestform" emweb_name="captive_portal_guest" method="post" title="Form used by guest users to login"><div id="emailbox">
                        <label for="email" accesskey="e">EMAIL</label>
                        <input id="email" name="email" size="25" class="text" accesskey="e" type="text">
                        </div><input name="cmd" value="authenticate" type="hidden">
                        <input name="Login" value="Log In" class="button" type="submit">
                        <p>Logging in as a guest user indicates you have        read and accepted the <a href="javascript:void(0)" onclick="showPolicy();">Acceptable Use Policy</a>.</p></form>
                </div><div id="instructions" style="display: none;">
                        <div id="instructionstext"></div>
                        <div id="instructionsbg"></div>
                </div></div></div></body></html>";

    /**
     * Tests the class against the real Starbucks(protal) webpage.
     * 
     * @throws Exception
     */
    @Test
    public void testWithRealHtml() throws Exception
    {
        HtmlForm htmlForm = new HtmlForm(new URL(url), html);

        Assert.assertEquals("http://nmd.sbx13386.philapa.wayport.net/connect.adp", htmlForm.actionUrl.toExternalForm());
        Assert.assertEquals("POST", htmlForm.method);

        Map<String, String> params = new HashMap<String, String>();
        params.put("NmdId", "27684");
        params.put("ReturnHost", "nmd.sbx13386.philapa.wayport.net");
        params.put("MacAddr", "00:22:43:05:F7:CD");
        params.put("IpAddr", "10.135.57.10");
        params.put("NduMacAddr", "00:21:D8:DC:98:F6");
        params.put("NduPort", "4");
        params.put("PortType", "Wireless");
        params.put("PortDesc", "FRCCStudent::AP1");
        params.put("UseCount", "");
        params.put("PaymentMethod", "Passthrough");
        params.put("ChargeAmount", "");
        params.put("Style", "ATT");
        params.put("vsgpId", "5095780c-6d49-11dd-b9bf-0090fb1eba3a");
        params.put("pVersion", "2");
        params.put("ValidationHash", "28ca6d9df10db5a1344885d2dd668339");
        params.put("origDest", "");
        params.put("ProxyHost", "");
        params.put("vsgId", "101559");
        params.put("ts", "1287671589");
        params.put("AUPConfirmed", "1");
        params.put("aupAgree", "1");
        Assert.assertEquals(params, htmlForm.parameters);
    }

    /**
     * Tests to make sure it throws the appropriate exceptions at the
     * appropriate times.
     * 
     * @throws Exception
     */
    @Test
    public void testErrors() throws Exception
    {
        URL url = new URL("http://www.example.com");

        // no "form" tag anywhere
        try
        {
            new HtmlForm(url, "<html><body>Hello world!</body></html>");
            Assert.fail();
        } catch(InvalidFormException e)
        {
            // this exception should be thrown
        } catch(MalformedURLException e)
        {
            Assert.fail();
        }

        // no "action" attribute
        try
        {
            new HtmlForm(url, "<form>");
            Assert.fail();
        } catch(InvalidFormException e)
        {
            // this exception should be thrown
        } catch(MalformedURLException e)
        {
            Assert.fail();
        }
    }

    /**
     * Tests other aspects of the class, feeding it specially-crafted input.
     * 
     * @throws Exception
     */
    @Test
    public void testMisc() throws Exception
    {
        URL url = new URL("http://www.example.com/foo/bar/");
        HtmlForm htmlForm;

        // there's no "method" attribute, so it should default to "GET"
        htmlForm = new HtmlForm(url, "<form action=\"index.html\">");
        Assert.assertEquals("http://www.example.com/foo/bar/index.html", htmlForm.actionUrl.toExternalForm());
        Assert.assertEquals("GET", htmlForm.method);
        Assert.assertEquals(0, htmlForm.parameters.size());

        // there is a "method" attribute, it should be parsed as all upper-case
        htmlForm = new HtmlForm(url, "<form action=\"index.html\" method=\"post\">");
        Assert.assertEquals("http://www.example.com/foo/bar/index.html", htmlForm.actionUrl.toExternalForm());
        Assert.assertEquals("POST", htmlForm.method);
        Assert.assertEquals(0, htmlForm.parameters.size());

        // "action" attribute is absolute
        htmlForm = new HtmlForm(url, "<form action=\"http://www.foobar.com/index.html\" method=\"post\">");
        Assert.assertEquals("http://www.foobar.com/index.html", htmlForm.actionUrl.toExternalForm());
        Assert.assertEquals("POST", htmlForm.method);
        Assert.assertEquals(0, htmlForm.parameters.size());

        // "action" attribute uses "../"
        htmlForm = new HtmlForm(url, "<form action=\"../index.html\" method=\"post\">");
        Assert.assertEquals("http://www.example.com/foo/index.html", htmlForm.actionUrl.toExternalForm());
        Assert.assertEquals("POST", htmlForm.method);
        Assert.assertEquals(0, htmlForm.parameters.size());

        // "action" attribute uses "/"
        htmlForm = new HtmlForm(url, "<form action=\"/index.html\" method=\"post\">");
        Assert.assertEquals("http://www.example.com/index.html", htmlForm.actionUrl.toExternalForm());
        Assert.assertEquals("POST", htmlForm.method);
        Assert.assertEquals(0, htmlForm.parameters.size());

        // switch order of attributes
        htmlForm = new HtmlForm(url, "<form method=\"post\" action=\"index.html\">");
        Assert.assertEquals("http://www.example.com/foo/bar/index.html", htmlForm.actionUrl.toExternalForm());
        Assert.assertEquals("POST", htmlForm.method);
        Assert.assertEquals(0, htmlForm.parameters.size());

        // there are attributes in the form tag which can be ignored, along with
        // multiple whitespace characters between attributes
        htmlForm = new HtmlForm(
                url,
                "<form  \t method=\"post\"  \n\n doubleQuotes=\"typical usage\"\t attrWithNoValue  noQuotes=uglyHtml  singleQuotes='an alternative' action=\"index.html\" \n attrWithNoValue2>");
        Assert.assertEquals("http://www.example.com/foo/bar/index.html", htmlForm.actionUrl.toExternalForm());
        Assert.assertEquals("POST", htmlForm.method);
        Assert.assertEquals(0, htmlForm.parameters.size());

        // there some parameters
        htmlForm = new HtmlForm(
                url,
                "<form action=\"index.html\" method=\"post\"><input type=\"hidden\" name=\"foo\" value=\"bar\" /><input type=\"hidden\" name='sq' value='single quotes' /><input type=\"hidden\" name=nq value=no-quotes /><input type=\"hidden\" value=\"noname\" /><input type=\"hidden\" name=\"novalue\" /><input name=\"notype\" value=\"thereisnotypeattribute\" /><input type=\"submit\" name=\"submit\" value=\"button\" />");
        Assert.assertEquals("http://www.example.com/foo/bar/index.html", htmlForm.actionUrl.toExternalForm());
        Assert.assertEquals("POST", htmlForm.method);
        Assert.assertEquals(5, htmlForm.parameters.size());
        Assert.assertEquals("bar", htmlForm.parameters.get("foo"));
        Assert.assertEquals("single quotes", htmlForm.parameters.get("sq"));
        Assert.assertEquals("no-quotes", htmlForm.parameters.get("nq"));
        Assert.assertEquals("", htmlForm.parameters.get("novalue"));
        Assert.assertEquals("thereisnotypeattribute", htmlForm.parameters.get("notype"));
        Assert.assertNull(htmlForm.parameters.get("submit"));

        // there is more than one form...it will only look at the attributes of
        // the first form, but will read all the parameters of all of the forms
        htmlForm = new HtmlForm(
                url,
                "<form action=\"index.html\" method=\"post\"><input type=\"hidden\" name=\"foo\" value=\"bar\" /><input type=\"hidden\" value=\"noname\" /><input type=\"hidden\" name=\"novalue\" /></form><form action=\"http://www.google.com\"><input name=\"param\" value=\"inanotherform\" />");
        Assert.assertEquals("http://www.example.com/foo/bar/index.html", htmlForm.actionUrl.toExternalForm());
        Assert.assertEquals("POST", htmlForm.method);
        Assert.assertEquals(3, htmlForm.parameters.size());
        Assert.assertEquals("bar", htmlForm.parameters.get("foo"));
        Assert.assertEquals("", htmlForm.parameters.get("novalue"));
        Assert.assertEquals("inanotherform", htmlForm.parameters.get("param"));
    }
}
